using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Linq;

namespace MouseKeyboardOperationSimulator
{
    /// <summary>
    /// 屏幕信息类，用于获取和处理屏幕分辨率及DPI缩放因子
    /// </summary>
    public class ScreenInfo
    {
        // Win32 API声明
        [DllImport("user32.dll")]
        private static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

        [DllImport("gdi32.dll")]
        private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

        private const int LOGPIXELSX = 88;  // 水平DPI
        private const int LOGPIXELSY = 90;  // 垂直DPI

        /// <summary>
        /// 获取屏幕分辨率宽度
        /// </summary>
        public static int ScreenWidth => Screen.PrimaryScreen.Bounds.Width;

        /// <summary>
        /// 获取屏幕分辨率高度
        /// </summary>
        public static int ScreenHeight => Screen.PrimaryScreen.Bounds.Height;

        /// <summary>
        /// 获取虚拟桌面的总宽度和高度
        /// </summary>
        public static Size VirtualDesktopSize
        {
            get
            {
                // 在扩展模式下，获取整个虚拟桌面的尺寸
                Rectangle virtualDesktop = SystemInformation.VirtualScreen;
                return new Size(virtualDesktop.Width, virtualDesktop.Height);
            }
        }

        /// <summary>
        /// 检查所有屏幕的DPI缩放因子是否相同
        /// </summary>
        /// <returns>如果所有屏幕的DPI缩放因子相同则返回true，否则返回false</returns>
        public static bool AreAllScreenDpiScalesEqual()
        {
            if (Screen.AllScreens.Length <= 1)
                return true;

            float firstDpiScale = GetDpiScaleForScreen(Screen.AllScreens[0]);
            for (int i = 1; i < Screen.AllScreens.Length; i++)
            {
                float dpiScale = GetDpiScaleForScreen(Screen.AllScreens[i]);
                if (Math.Abs(dpiScale - firstDpiScale) > 0.01f)
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 获取指定屏幕的DPI缩放因子
        /// </summary>
        /// <param name="screen">屏幕对象</param>
        /// <returns>DPI缩放因子</returns>
        private static float GetDpiScaleForScreen(Screen screen)
        {
            // 注意：这种方法在不同.NET版本和Windows版本中可能有局限性
            // 更精确的方法需要使用Windows API或WPF的PresentationSource
            IntPtr hdc = GetDC(IntPtr.Zero);
            try
            {
                int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
                return dpiX / 96.0f;
            }
            finally
            {
                ReleaseDC(IntPtr.Zero, hdc);
            }
        }

        /// <summary>
        /// 获取所有屏幕的信息（包括位置）
        /// </summary>
        /// <returns>所有屏幕信息的列表</returns>
        public static List<ScreenInfoData> GetAllScreenInfoWithPosition()
        {
            List<ScreenInfoData> screenInfos = new List<ScreenInfoData>();
            
            foreach (Screen screen in Screen.AllScreens)
            {
                IntPtr hdc = GetDC(IntPtr.Zero);
                try
                {
                    int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
                    float dpiScale = dpiX / 96.0f;
                    screenInfos.Add(new ScreenInfoData
                    {
                        Width = screen.Bounds.Width,
                        Height = screen.Bounds.Height,
                        DpiScale = dpiScale,
                        X = screen.Bounds.X,
                        Y = screen.Bounds.Y,
                        IsPrimary = screen.Primary
                    });
                }
                finally
                {
                    ReleaseDC(IntPtr.Zero, hdc);
                }
            }
            
            return screenInfos;
        }

        /// <summary>
        /// 获取所有屏幕的信息
        /// </summary>
        /// <returns>所有屏幕信息的列表</returns>
        public static List<string> GetAllScreenInfo()
        {
            List<string> screenInfos = new List<string>();
            
            foreach (Screen screen in Screen.AllScreens)
            {
                IntPtr hdc = GetDC(IntPtr.Zero);
                try
                {
                    int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
                    float dpiScale = dpiX / 96.0f;
                    screenInfos.Add(screen.Bounds.Width + "x" + screen.Bounds.Height + "@" + dpiScale.ToString("F2"));
                }
                finally
                {
                    ReleaseDC(IntPtr.Zero, hdc);
                }
            }
            
            return screenInfos;
        }

        /// <summary>
        /// 获取鼠标所在屏幕的分辨率和DPI信息
        /// </summary>
        /// <param name="mouseX">鼠标X坐标</param>
        /// <param name="mouseY">鼠标Y坐标</param>
        /// <returns>屏幕信息字符串</returns>
        public static string GetScreenInfoAtMousePosition(int mouseX, int mouseY)
        {
            // 查找包含鼠标坐标的屏幕
            foreach (Screen screen in Screen.AllScreens)
            {
                if (screen.Bounds.Contains(mouseX, mouseY))
                {
                    // 获取该屏幕的DPI信息
                    IntPtr hdc = GetDC(IntPtr.Zero);
                    try
                    {
                        int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
                        float dpiScale = dpiX / 96.0f;
                        return screen.Bounds.Width + "x" + screen.Bounds.Height + "@" + dpiScale.ToString("F2");
                    }
                    finally
                    {
                        ReleaseDC(IntPtr.Zero, hdc);
                    }
                }
            }
            
            // 如果没有找到匹配的屏幕，返回主屏幕信息
            return GetScreenInfoString();
        }

        /// <summary>
        /// 获取屏幕DPI缩放因子（相对于96 DPI的标准值）
        /// </summary>
        public static float DpiScaleFactor
        {
            get
            {
                IntPtr hdc = GetDC(IntPtr.Zero);
                try
                {
                    int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
                    return dpiX / 96.0f;
                }
                finally
                {
                    ReleaseDC(IntPtr.Zero, hdc);
                }
            }
        }

        /// <summary>
        /// 获取屏幕信息字符串（分辨率和DPI缩放因子）
        /// </summary>
        public static string GetScreenInfoString()
        {
            // 检查是否处于扩展显示模式
            if (Screen.AllScreens.Length > 1)
            {
                // 检查所有屏幕的DPI缩放因子是否相同
                if (AreAllScreenDpiScalesEqual())
                {
                    // 如果所有屏幕的缩放比例相同，则只记录虚拟屏幕信息
                    Rectangle virtualScreen = SystemInformation.VirtualScreen;
                    return virtualScreen.Width + "x" + virtualScreen.Height + "@" + DpiScaleFactor.ToString("F2");
                }
                else
                {
                    // 如果缩放比例不同，则列出所有屏幕信息
                    var screenInfos = GetAllScreenInfo();
                    return string.Join(";", screenInfos);
                }
            }
            else
            {
                // 在单屏模式下，返回主屏幕的信息
                return ScreenWidth + "x" + ScreenHeight + "@" + DpiScaleFactor.ToString("F2");
            }
        }

        /// <summary>
        /// 根据源屏幕信息和目标屏幕信息重新计算坐标
        /// </summary>
        /// <param name="originalCoordinate">原始坐标 (x,y)</param>
        /// <param name="sourceScreenInfo">源屏幕信息 (格式: "宽度x高度@缩放因子")</param>
        /// <param name="targetScreenInfo">目标屏幕信息 (格式: "宽度x高度@缩放因子")</param>
        /// <returns>重新计算后的坐标</returns>
        public static string RecalculateCoordinate(string originalCoordinate, string sourceScreenInfo, string targetScreenInfo)
        {
            // 解析原始坐标
            var parts = originalCoordinate.Split(',');
            if (parts.Length != 2)
                return originalCoordinate;

            if (!int.TryParse(parts[0].Trim(), out int originalX) ||
                !int.TryParse(parts[1].Trim(), out int originalY))
                return originalCoordinate;

            // 解析源屏幕信息
            // 检查是否包含多个屏幕信息（用分号分隔）
            string[] sourceScreens = sourceScreenInfo.Split(';');
            string[] targetScreens = targetScreenInfo.Split(';');
            
            // 如果只有一个屏幕信息，则使用原有逻辑
            if (sourceScreens.Length == 1 && targetScreens.Length == 1)
            {
                return RecalculateCoordinateSingleScreen(originalX, originalY, sourceScreenInfo, targetScreenInfo);
            }
            else
            {
                // 处理多屏幕信息的情况
                // 获取屏幕信息（包括位置）
                var sourceScreensWithPosition = GetAllScreenInfoWithPosition();
                
                // 根据原始坐标确定它属于哪个屏幕
                var sourceScreen = FindScreenForCoordinate(originalX, originalY, sourceScreensWithPosition);
                
                // 如果找不到对应的屏幕，使用第一个屏幕
                if (sourceScreen == null)
                    sourceScreen = sourceScreensWithPosition.FirstOrDefault();
                
                // 获取目标屏幕信息
                var targetScreensWithPosition = GetAllScreenInfoWithPosition();
                var targetScreen = targetScreensWithPosition.FirstOrDefault();
                
                // 如果找到了源屏幕和目标屏幕，则根据它们的位置调整坐标
                if (sourceScreen != null && targetScreen != null)
                {
                    // 调整原始坐标，使其相对于源屏幕的原点
                    int relativeX = originalX - sourceScreen.X;
                    int relativeY = originalY - sourceScreen.Y;
                    
                    // 使用相对坐标进行重新计算
                    string recalculatedRelativeCoordinate = RecalculateCoordinateSingleScreen(relativeX, relativeY, 
                        $"{sourceScreen.Width}x{sourceScreen.Height}@{sourceScreen.DpiScale}", 
                        $"{targetScreen.Width}x{targetScreen.Height}@{targetScreen.DpiScale}");
                    
                    // 将重新计算后的相对坐标转换为目标屏幕的绝对坐标
                    var recalculatedParts = recalculatedRelativeCoordinate.Split(',');
                    if (recalculatedParts.Length == 2 && 
                        int.TryParse(recalculatedParts[0], out int newRelativeX) &&
                        int.TryParse(recalculatedParts[1], out int newRelativeY))
                    {
                        int newAbsoluteX = newRelativeX + targetScreen.X;
                        int newAbsoluteY = newRelativeY + targetScreen.Y;
                        return $"{newAbsoluteX},{newAbsoluteY}";
                    }
                }
                
                // 如果无法准确计算，回退到简单的方法
                string sourceScreenInfoStr = sourceScreens[0];
                string targetScreenInfoStr = targetScreens[0];
                return RecalculateCoordinateSingleScreen(originalX, originalY, sourceScreenInfoStr, targetScreenInfoStr);
            }
        }

        /// <summary>
        /// 根据坐标查找所属的屏幕
        /// </summary>
        /// <param name="x">X坐标</param>
        /// <param name="y">Y坐标</param>
        /// <param name="screens">屏幕信息列表</param>
        /// <returns>包含坐标的屏幕信息</returns>
        private static ScreenInfoData FindScreenForCoordinate(int x, int y, List<ScreenInfoData> screens)
        {
            foreach (var screen in screens)
            {
                if (x >= screen.X && x < screen.X + screen.Width &&
                    y >= screen.Y && y < screen.Y + screen.Height)
                {
                    return screen;
                }
            }
            return null;
        }

        /// <summary>
        /// 根据单个源屏幕信息和单个目标屏幕信息重新计算坐标
        /// </summary>
        /// <param name="originalX">原始X坐标</param>
        /// <param name="originalY">原始Y坐标</param>
        /// <param name="sourceScreenInfo">源屏幕信息 (格式: "宽度x高度@缩放因子")</param>
        /// <param name="targetScreenInfo">目标屏幕信息 (格式: "宽度x高度@缩放因子")</param>
        /// <returns>重新计算后的坐标</returns>
        private static string RecalculateCoordinateSingleScreen(int originalX, int originalY, string sourceScreenInfo, string targetScreenInfo)
        {
            // 解析源屏幕信息
            var sourceScreenParts = sourceScreenInfo.Split('@');
            if (sourceScreenParts.Length != 2)
                return $"{originalX},{originalY}";

            var sourceResolutionParts = sourceScreenParts[0].Split('x');
            if (sourceResolutionParts.Length != 2)
                return $"{originalX},{originalY}";

            if (!int.TryParse(sourceResolutionParts[0], out int sourceWidth) ||
                !int.TryParse(sourceResolutionParts[1], out int sourceHeight) ||
                !float.TryParse(sourceScreenParts[1], out float sourceDpiScale))
                return $"{originalX},{originalY}";

            // 解析目标屏幕信息
            var targetScreenParts = targetScreenInfo.Split('@');
            if (targetScreenParts.Length != 2)
                return $"{originalX},{originalY}";

            var targetResolutionParts = targetScreenParts[0].Split('x');
            if (targetResolutionParts.Length != 2)
                return $"{originalX},{originalY}";

            if (!int.TryParse(targetResolutionParts[0], out int targetWidth) ||
                !int.TryParse(targetResolutionParts[1], out int targetHeight) ||
                !float.TryParse(targetScreenParts[1], out float targetDpiScale))
                return $"{originalX},{originalY}";

            // 如果分辨率和DPI都相同，则无需重新计算
            if (sourceWidth == targetWidth && sourceHeight == targetHeight && 
                Math.Abs(sourceDpiScale - targetDpiScale) < 0.01f)
                return $"{originalX},{originalY}";

            // 重新计算坐标
            // 先根据分辨率比例调整，再根据DPI缩放因子调整
            float xRatio = (float)targetWidth / sourceWidth;
            float yRatio = (float)targetHeight / sourceHeight;
            float dpiRatio = targetDpiScale / sourceDpiScale;

            int newX = (int)(originalX * xRatio * dpiRatio);
            int newY = (int)(originalY * yRatio * dpiRatio);

            return $"{newX},{newY}";
        }
    }

    /// <summary>
    /// 屏幕信息数据结构
    /// </summary>
    public class ScreenInfoData
    {
        public int Width { get; set; }
        public int Height { get; set; }
        public float DpiScale { get; set; }
        public int X { get; set; }
        public int Y { get; set; }
        public bool IsPrimary { get; set; }
    }
}